Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: QuickTime Components /
Chapter 5 - Sequence Grabber Components


Using Sequence Grabber Components

You can use the sequence grabber component to play captured data for the user or to save captured data in a QuickTime movie. The sequence grabber component provides functions that give your application precise control over the display of the captured data.

This section describes how to use the basic sequence grabber component functions as well as the functions that allow you to configure video and sound channels.

Sequence grabber components are standard components that are managed by the Component Manager. See the chapter "Component Manager" in Inside Macintosh: More Macintosh Toolbox for more information about the Component Manager and about how to use components.

Apple has defined a component type value for sequence grabber components--that type value is 'barg'. You can use the following constant to specify this type value.

#define SeqGrabComponentType 'barg' /* sequence grabber
                                       component type */
Apple has defined a functional interface for basic sequence grabber components. For information about the functions a sequence grabber component may support, see "Sequence Grabber Component Functions," which begins on page 5-22.

You can use the following constants to refer to the request codes for each of the functions that a sequence grabber component may support.

enum {
   /* selectors for basic sequence grabber component functions */
   kSGInitializeSelect              = 0x1;   /* SGInitialize */
   kSGSetDataOutputSelect           = 0x2;   /* SGSetDataOutput */
   kSGGetDataOutputSelect           = 0x3;   /* SGGetDataOutput */
   kSGSetGWorldSelect               = 0x4;   /* SGSetGWorld */
   kSGGetGWorldSelect               = 0x5;   /* SGGetGWorld */ 
   kSGNewChannelSelect              = 0x6;   /* SGNewChannel */
   kSGDisposeChannelSelect          = 0x7;   /* SGDisposeChannel */
   kSGStartPreviewSelect            = 0x10;  /* SGStartPreview */
   kSGStartRecordSelect             = 0x11;  /* SGStartRecord */
   kSGIdleSelect                    = 0x12;  /* SGIdle */
   kSGStopSelect                    = 0x13;  /* SGStop */
   kSGPauseSelect                   = 0x14;  /* SGPause */
   kSGPrepareSelect                 = 0x15;  /* SGPrepare */
   kSGReleaseSelect                 = 0x16;  /* SGRelease */
   kSGGetMovieSelect                = 0x17;  /* SGGetMovie */
   kSGSetMaximumRecordTimeSelect    = 0x18;  /* SGSetMaximumRecordTime */
   kSGGetMaximumRecordTimeSelect    = 0x19;  /* SGGetMaximumRecordTime */
   kSGGetStorageSpaceRemainingSelect= 0x1a;  /* SGGetStorageSpaceRemaining */
         
   kSGGetTimeRemainingSelect        = 0x1b;  /* SGGetTimeRemaining */   
   kSGGrabPictSelect                = 0x1c;  /* SGGrabPict */
   kSGGetLastMovieResIDSelect       = 0x1d;  /* SGGetLastMovieResID */
   kSGSetFlagsSelect                = 0x1e;  /* SGSetFlags */
   kSGGetFlagsSelect                = 0x1f;  /* SGGetFlags */
   kSGSetDataProcSelect             = 0x20;  /* SGSetDataProc */
   kSGNewChannelFromComponentSelect = 0x21;  /* SGNewChannelFromComponent */
   kSGDisposeDeviceListSelect       = 0x22;  /* SGDisposeDeviceList */
   kSGAppendDeviceListToMenuSelect  = 0x23;  /* SGAppendDeviceListToMenu */
   kSGSetSettingsSelect             = 0x24;  /* SGSetSettings */
   kSGGetSettingsSelect             = 0x25;  /* SGGetSettings */
   kSGGetIndChannelSelect           = 0x26;  /* SGGetIndChannel */
   kSGUpdateSelect                  = 0x27;  /* SGUpdate */
   kSGGetPauseSelect                = 0x28;  /* SGGetPause */
   kSGSettingsDialogSelect          = 0x29;  /* SGSettingsDialog */
   kSGGetAlignmentProcSelect        = 0x2A;  /* SGGetAlignmentProc */
   kSGSetChannelSettingsSelect      = 0x2B;  /* SGSetChannelSettings */
   kSGGetChannelSettingsSelect      = 0x2C;  /* SGGetChannelSettings */

   /* selectors for common channel configuration functions */
   kSGCSetChannelUsageSelect        = 0x80;  /* SGCSetChannelUsage */
   kSGCGetChannelUsageSelect        = 0x81;  /* SGCGetChannelUsage */
   kSGCSetChannelBoundsSelect       = 0x82;  /* SGCSetChannelBounds */
   kSGCGetChannelBoundsSelect       = 0x83;  /* SGCGetChannelBounds */
   kSGCSetChannelVolumeSelect       = 0x84;  /* SGCSetChannelVolume */
   kSGCGetChannelVolumeSelect       = 0x85;  /* SGCGetChannelVolume */
   kSGCGetChannelInfoSelect         = 0x86;  /* SGCGetChannelInfo */
   kSGCSetChannelPlayFlagsSelect    = 0x87;  /* SGCSetChannelPlayFlags */  
   kSGCGetChannelPlayFlagsSelect    = 0x88;  /* SGCGetChannelPlayFlags */
   kSGCSetChannelMaxFramesSelect    = 0x89;  /* SGCSetChannelMaxFrames */
   kSGCGetChannelMaxFramesSelect    = 0x8a;  /* SGCGetChannelMaxFrames */
   kSGCSetChannelRefConSelect       = 0x8b;  /* SGCSetChannelRefCon */
   kSGCSetChannelClipSelect         = 0x8C;  /* SGCSetChannelClip */
   kSGCGetChannelClipSelect         = 0x8D;  /* SGCGetChannelClip */
   kSGCGetChannelSampleDescriptionSelect = 0x8E;
                                       /* SGCGetChannelSampleDescription */
   kSGCGetChannelDeviceListSelect   = 0x8F;  /* SGCGetChannelDeviceList */
   kSGCSetChannelDeviceSelect       = 0x90;  /* SGCSetChannelDevice */
   kSGCSetChannelMatrixSelect       = 0x91;  /* SGCSetChannelMatrix */
   kSGCGetChannelMatrixSelect       = 0x92;  /* SGCGetChannelMatrix */
   kSGCGetChannelTimeScaleSelect    = 0x93;  /* SGCGetChannelTimeScale */

   /* selectors for video channel configuration functions */
   kSGCGetSrcVideoBoundsSelect      = 0x100; /* SGCGetSrcVideoBounds */
   kSGCSetVideoRectSelect           = 0x101; /* SGCSetVideoRect */
   kSGCGetVideoRectSelect           = 0x102; /* SGCGetVideoRect */
   kSGCGetVideoCompressorTypeSelect = 0x103; /* SGCGetVideoCompressorType */
   kSGCSetVideoCompressorTypeSelect = 0x104; /* SGCSetVideoCompressorType */
   kSGCSetVideoCompressorSelect     = 0x105; /* SGCSetVideoCompressor */
   kSGCGetVideoCompressorSelect     = 0x106; /* SGCGetVideoCompressor */
   kSGCGetVideoDigitizerComponentSelect
                                    = 0x107;
                                          /* SGCGetVideoDigitizerComponent */
   kSGCSetVideoDigitizerComponentSelect
                                    = 0x108; 
                                          /* SGCSetVideoDigitizerComponent */
   kSGCVideoDigitizerChangedSelect  = 0x109; /* SGCVideoDigitizerChanged */
   kSGCSetVideoBottlenecksSelect    = 0x10a; /* SGCSetVideoBottlenecks */
   kSGCGetVideoBottlenecksSelect    = 0x10b; /* SGCGetVideoBottlenecks */
   kSGCGrabFrameSelect              = 0x10c; /* SGCGrabFrame */
   kSGCGrabFrameCompleteSelect      = 0x10d; /* SGCGrabFrameComplete */
   kSGCDisplayFrameSelect           = 0x10e; /* SGCDisplayFrame */
   kSGCCompressFrameSelect          = 0x10f; /* SGCCompressFrame */
   kSGCCompressFrameCompleteSelect  = 0x110; /* SGCCompressFrameComplete */
   kSGCAddFrameSelect               = 0x111; /* SGCAddFrameSelect */
   kSGCTransferFrameForCompressSelect = 0x112; 
                                          /* SGCTransferFrameForCompress */
   kSGCSetCompressBufferSelect      = 0x113; /* SGCSetCompressBuffer */
   kSGCGetCompressBufferSelect      = 0x114; /* SGCGetCompressBuffer */
   kSGCGetBufferInfoSelect          = 0x115; /* SGCGetBufferInfo */
   kSGCSetUseScreenBufferSelect     = 0x116; /* SGCSetUseScreenBuffer */
   kSGCGetUseScreenBufferSelect     = 0x117; /* SGCGetUseScreenBuffer */
   kSGCGrabCompressCompleteSelect   = 0x118; /* SGCGrabCompressComplete */
   kSGCDisplayCompressSelect        = 0x119; /* SGCDisplayCompress */
   kSGCSetFrameRateSelect           = 0x11A; /* SGCSetFrameRate */
   kSGCGetFrameRateSelect           = 0x11B; /* SGCGetFrameRate */ 

   /* selectors for sound channel configuration functions */
   kSGCSetSoundInputDriverSelect     = 0x100;/* SGCSetSoundInputDriver */
   kSGCGetSoundInputDriverSelect     = 0x101;/* SGCGetSoundInputDriver */
   kSGCSoundInputDriverChangedSelect = 0x102;
                                       /* SGCSoundInputDriverChanged */
   kSGCSetSoundRecordChunkSizeSelect = 0x103;
                                       /* SGCSetSoundRecordChunkSize */
   kSGCGetSoundRecordChunkSizeSelect = 0x104;
                                       /* SGCGetSoundRecordChunkSize */
   kSGCSetSoundInputRateSelect       = 0x105; /* SGCSetSoundInputRate */
   kSGCGetSoundInputRateSelect       = 0x106; /* SGCGetSoundInputRate */
   kSGCSetSoundInputParametersSelect = 0x107; 
                                       /* SGCSetSoundInputParameters */
   kSGCGetSoundInputParametersSelect = 0x108;
                                       /* SGCGetSoundInputParameters */
   /* selectors for utility functions provided to channel components */
   kSGWriteMovieData                = 0x100; /* SGWriteMovieData */
   kSGAddFrameReferenceSelect       = 0x101; /* SGAddFrameReference */
   kSGGetNextFrameReferenceSelect   = 0x102; /* SGGetNextFrameReference */
   kSGGetTimeBaseSelect             = 0x103; /* SGGetTimeBase */
   kSGSortDeviceListSelect          = 0x104; /* SGSortDeviceList */
   kSGAddMovieDataSelect            = 0x105; /* SGAddMovieData */
   kSGChangedSourceSelect           = 0x106; /* SGChangedSource */
};

Previewing and Recording Captured Data

You can use sequence grabber components in two ways: to play digitized data for the user or to save captured data in a QuickTime movie. The process of displaying data that is to be captured is called previewing; saving captured data in a movie is called recording. You can use previewing to allow the user to prepare to make a recording. If you do so, your application can move directly from the preview operation to a record operation, without stopping the process.

Previewing

Previewing captured data involves playing that data for the user as it is captured. For video data, this means displaying the video images on the computer screen. For audio data, this means playing the sound through the computer's sound system. The following paragraphs outline the steps you must follow to preview captured data.

  1. First, you must open a connection to the sequence grabber component. Use the Component Manager's OpenDefaultComponent or OpenComponent function.
  2. Once you have a connection to a sequence grabber component, you must configure the component for the preview operation. Use the SGSetGWorld function (described on page 5-27) to set the graphics world in which the preview is to be displayed. Allocate the appropriate channels by calling the SGNewChannel function (described on page 5-29). You must call this function once for each channel to be used by the sequence grabber component. Use the SGSetChannelUsage function (described on page 5-56) to specify that each channel is to be used for previewing. You can then use the appropriate channel configuration functions to prepare the channel for the preview operation. For video channels, use the functions discussed in "Working With Video Channels" beginning on page 5-73. For sound channels, use the functions discussed in "Working With Sound Channels" beginning on page 5-88.
  3. You start the preview operation by calling the SGStartPreview function (see page 5-35). The sequence grabber component then begins collecting data from the channels that you have created and plays that data appropriately. You can pause and restart the preview by calling the SGPause function (see page 5-38). Use the SGStop function (see page 5-38) to stop the preview. During the preview operation, be sure to call the SGIdle function (see page 5-36) frequently, so that the sequence grabber and its channels can perform the operation.
  4. When you are done previewing, you can start recording or close your connection to the sequence grabber component. When you close the sequence grabber component, it automatically disposes of the channels you created.

See the sample program in Listing 5-1 on page 5-10 for an example of the preview operation.

Recording

During a record operation, a sequence grabber component collects the data it captures and formats that data into a QuickTime movie. During a record operation, the sequence grabber can also play the captured data for the user. However, the sequence grabber tries to prevent the playback from interfering with the quality of the recording process.

The following paragraphs discuss the steps you must follow to record captured data.

  1. As with a preview operation, your application must establish a
    connection to a sequence grabber component. Use the Component
    Manager's OpenDefaultComponent or OpenComponent function.
  2. Once you have a connection to a sequence grabber component, you must configure the component for the record operation. Use the SGSetGWorld function (see page 5-27) to set the graphics world in which the data is to be displayed. Allocate the appropriate channels by calling the SGNewChannel function (see page 5-29). You must call this function once for each channel to be used by the sequence grabber component. Use the SGSetChannelUsage function (see page 5-56) to specify that each channel is to be used for recording. At this time, you can specify whether the sequence grabber is to play that channel's data while recording. You can then use the appropriate channel configuration functions to prepare the channel for the record operation. For video channels, use the functions discussed in "Working With Video Channels" beginning on page 5-73. For sound channels, use the functions discussed in "Working With Sound Channels" beginning on page 5-88.
  3. You must specify a movie file for use by the sequence grabber during the record operation. Use the SGSetDataOutput function (see page 5-24) to specify this movie file. This function also allows you to control whether the sequence grabber adds the movie resource to the movie file and whether it replaces existing data or appends the new movie to the file.
  4. You can limit the amount of data that is captured during a record operation. The SGSetMaximumRecordTime function (see page 5-51) establishes a time limit for the record operation. The SGSetChannelMaxFrames function (see page 5-60) limits the number of frames of data that the sequence grabber collects from a specific channel.
  5. You start the record operation by calling the SGStartRecord function (see page 5-35). The sequence grabber component then begins collecting data from the channels you have created, stores the data in a QuickTime movie, and, optionally, plays that data appropriately. You can pause and restart the record process by calling the SGPause function (see page 5-38). During the record operation, be sure to call the SGIdle function (see page 5-36) frequently, so that the sequence grabber and its channels can perform the operation. Use the SGStop function (see page 5-38) to stop recording. At this time, the sequence grabber saves the movie in your movie file, if you have chosen to do so.
  6. When you are done recording, you can go back to previewing or close your connection to the sequence grabber component. When you close the sequence grabber component, it automatically disposes of the channels you created as well as any movies it has created.

Playing Captured Data and Saving It in a QuickTime Movie

This section supplies a sample program that shows how to use a sequence grabber component to preview and record captured data. The program is divided into groups of functions that do the following tasks:

Initializing a Sequence Grabber Component

Listing 5-1 provides a sample function that creates and initializes a default sequence grabber component for a specified window (using the OpenDefaultComponent and SGInitialize functions, respectively). It then sets the graphics world of the sequence grabber component to the specified window with the SGSetGWorld function. Note that the CloseComponent function is called for housekeeping purposes in case the sequence grabber component fails. For more on OpenDefaultComponent and CloseComponent, see the chapter "Component Manager" in Inside Macintosh: More Macintosh Toolbox. For details on SGInitialize and SGSetGWorld, see page 5-23 and page 5-27, respectively.

Listing 5-1 Initializing a sequence grabber component

SeqGrabComponent MakeSequenceGrabber (WindowPtr aWindow)
{
   SeqGrabComponent anSG;
   OSErr err = noErr;

   /* open up the default sequence grabber */
   anSG = OpenDefaultComponent (SeqGrabComponentType, 0);
   if (anSG) {
      /* initialize the default sequence grabber component */
      err = SGInitialize (anSG);
      if (!err) {
      /* set the sequence grabber's graphics world to the 
         specified window */
         err = SGSetGWorld (anSG, (CGrafPtr) aWindow, nil);
      }
   }
   if (err && anSG) {
      /* clean up on failure */
      CloseComponent (anSG);
      anSG = nil;
   }
   return anSG;
}

Creating a Sound Channel and a Video Channel

Listing 5-2 supplies a sample function that attempts to create a video channel and a sound channel for the sequence grabber component that was created in Listing 5-1. The boundaries of the video channel are set to the specifications of the bounds parameter. The channel's usage is always set to allow previewing. If the value of the willRecord parameter is true, then the usage of the channel is set to allow recording also.

The SGNewChannel function (described on page 5-29) uses the VideoMediaType constant to create a video channel and the SoundMediaType constant to create a sound channel. The SGSetChannelBounds function (described on page 5-62) specifies the boundaries of the video channel. The SGSetChannelUsage function (described on page 5-56) specifies whether the video and the sound channels are used for preview or record operations. The SGDisposeChannel function (described on page 5-32) cleans up upon failure for each of the channels.

Listing 5-2 Creating a sound channel and a video channel

void MakeGrabChannels (SeqGrabComponent anSG, 
                        SGChannel *videoChannel, 
                        SGChannel *soundChannel,
                        const Rect *bounds, Boolean willRecord)
{
   OSErr err;
   long usage;
   /* figure out the usage */
   usage = seqGrabPreview;       /* always previewing */
   if (willRecord)
      usage |= seqGrabRecord;    /* sometimes recording */
   /* create a video channel */
   err = SGNewChannel (anSG, VideoMediaType, videoChannel);
   if (!err) {

   /* set boundaries for new video channel */
      err = SGSetChannelBounds (*videoChannel, bounds);
   /* set usage for new video channel */
      if (!err)
         err = SGSetChannelUsage (*videoChannel, 
                                 usage | seqGrabPlayDuringRecord);
      if (err) {

         /* clean up on failure */
         SGDisposeChannel (anSG, *videoChannel);
         *videoChannel = nil;
      }
   }
   /* create a sound channel */
   err = SGNewChannel (anSG, SoundMediaType, soundChannel);
   if (!err) {

      /* set usage of new sound channel */
      err = SGSetChannelUsage (*soundChannel, usage);
      if (err) {

         /* clean up on failure */
         SGDisposeChannel(anSG, *soundChannel);
         *soundChannel = nil;
      }
   }
}

Previewing Sound and Video Sequences in a Window

Listing 5-3 shows how to use the sequence grabber component to preview sound and video sequences in a window. Clicking the content area of the window causes the sequence grabber to pause until the mouse button is released.

The Image Compression Manager's GetBestDeviceRect function helps you determine the best monitor for the window. The SGStartPreview function (described on page 5-35) begins the preview of the sound and video sequences. The SGIdle function (described on page 5-36) grants the sequence grabber component the time it needs to preview data. The SGUpdate function (described on page 5-37) informs the sequence grabber of the update event. The Window Manager's BeginUpdate and EndUpdate functions respond to the event. The SGPause function (described on page 5-38) instructs the sequence grabber to suspend and resume its preview operation. In this example, it is used to suspend the preview operation while the mouse button is held down. Finally, the SGStop function (described on page 5-38) halts the action of the sequence grabber component. The Component Manager's CloseComponent function closes the component connection. The Window Manager's DisposeWindow function disposes of the window.

Listing 5-3 Previewing sound and video sequences in a window

void CheckError(OSErr error, Str255 displayString)
{
   if (error == noErr) return;
   if (displayString[0] > 0) 
      DebugStr(displayString);
   ExitToShell();
}

Boolean IsQuickTimeInstalled (void) 
{
   short    error;
   long     result;
   error = Gestalt (gestaltQuickTime, &result);
   return (error == noErr);
}

void initialize (void)
{
   OSErr err;
   InitGraf (&qd.thePort);
   InitFonts ();
   InitWindows ();
   InitMenus ();
   TEInit ();
   InitDialogs (nil);
   MaxApplZone();

   if (!IsQuickTimeInstalled())
      CheckError(-1,"\pPlease install QuickTime and try again.");

   err = EnterMovies ();
   CheckError(err,"\pUnable to initialize Movie Toolbox.");
}
WindowPtr makeWindow(void)
{
   WindowPtr aWindow;
   Rect windowRect = {0, 0, 120, 160};
   Rect bestRect;

   /* figure out the best monitor for the window */
   GetBestDeviceRect (nil, &bestRect);

   /* put the window in the top left corner of that monitor */
   OffsetRect(&windowRect, bestRect.left + 10, bestRect.top + 50);

   /* create the window */
   aWindow = NewCWindow (nil, &windowRect, "\pGrabber",
                         true, noGrowDocProc, (WindowPtr)-1,
                         true, 0);

   /* and set the port to the new window */
   SetPort(aWindow);

   return aWindow;
}
main (void)
{
   WindowPtr theWindow;
   SeqGrabComponent theSG;
   SGChannel videoChannel, soundChannel;
   Boolean done = false;
   OSErr err;

   initialize();
   theWindow = makeWindow();
   theSG = makeSequenceGrabber(theWindow);
   if (!theSG) return;

   makeGrabChannels(theSG, &videoChannel, &soundChannel,
                   &theWindow->portRect, false);
   if ((videoChannel == nil) && (soundChannel == nil))
      CheckError(-1,"\pNo sound or video available.");

   err = SGStartPreview(theSG);
   CheckError(err, "\pCan't start preview");

   while (!done) {
      AlignmentProcRecord alignProc;
      short part;
      WindowPtr whichWindow;
      EventRecord theEvent;
      GetNextEvent(everyEvent, &theEvent);

      switch (theEvent.what) {
         case nullEvent:   /* give the sequence grabber time */
               err = SGIdle (theSG);
               if (err) done = true;
               break;

         case updateEvt:if (theEvent.message == (long)theWindow) {
                           /* inform the sequence grabber of the
                              update */
            SGUpdate(theSG,((WindowPeek)
                            theWindow)->updateRgn);
            /* and swallow the update event */
            BeginUpdate(theWindow);
            EndUpdate(theWindow);
         }
         break;
         
         case mouseDown:part = FindWindow (theEvent.where, 
                                          &whichWindow);
               if (whichWindow != theWindow) break;
               switch (part) {
                  case inContent:
                     /* pause until mouse button is 
                        released */
                     SGPause (theSG, true);
                     while (StillDown())
                     ;
                     SGPause(theSG, false);
                     break;
                  case inGoAway:
                     done = TrackGoAway (theWindow,
                                       theEvent.where);
                     break;
                  case inDrag:
                     /* pause when dragging window so video 
                        doesn't draw in the wrong place */
                     SGPause (theSG, true);
                     SGGetAlignmentProc (theSG, &alignProc);
                     DragAlignedWindow (theWindow,
                                        theEvent.where,
                                        &screenBits.bounds,
                                        nil, &alignProc);
                     SGPause (theSG, false);
                     break;
                  }
                  break;
      }
   }
   /* clean up */
   SGStop (theSG);
   CloseComponent (theSG);
   DisposeWindow (theWindow);
}

Capturing Sound and Video Data

Listing 5-4 uses the sequence grabber component to capture ten seconds of
sound and video data. It prompts the user for the name of the file to create. The SGSettingsDialog function (described on page 5-45) is issued to invoke the default sound and video capture settings dialog boxes. These default dialog boxes allow the user to configure the settings for the capture operations. The SGSetMaximumRecordTime function (described on page 5-51) indicates how long the capture operations will last. The SGStartRecord function (described on page 5-35) specifies the time at which the capture operations will begin. The SGIdle function (described on page 5-36) grants the time needed to confirm the capture operations. Finally, the SGStop function (described on page 5-38) and the Window Manager's DisposeWindow routine are called in order to complete the capture of the sequences.

Listing 5-4 Capturing sound and video

main (void)
{
   WindowPtr theWindow;
   CGrafPort tempPort;
   SeqGrabComponent theSG;
   SGChannel videoChannel, soundChannel;
   OSErr err;

   initialize();
   theWindow = makeWindow();

   theSG = makeSequenceGrabber(theWindow);
   if (!theSG) return;
   err = setGrabFile(theSG);
   CheckError(err, "\pNo output file");

   makeGrabChannels (theSG, &videoChannel, &soundChannel,
                      &theWindow->portRect, true);
   if ((videoChannel == nil) && (soundChannel == nil))
      CheckError(-1,"\pNo sound or video available.");
   if (videoChannel)
      SGSettingsDialog (theSG, videoChannel, 0, nil,
                         DoTheRightThing, nil, 0);
   if (soundChannel)
      SGSettingsDialog(theSG, soundChannel, 0, nil,
                      DoTheRightThing, nil, 0);

   err = SGSetMaximumRecordTime(theSG, 10 * 60);
   CheckError(err, "\pCan't set max record time");

   err = SGStartRecord (theSG);
   CheckError(err, "\pCan't start record");

   while (!err)
      err = SGIdle (theSG);
   if (err == grabTimeComplete)
      err = noErr;
   CheckError(err, "\pError while recording");

   err = SGStop(theSG);
   CheckError(err, "\pError creating movie");

   CloseComponent(theSG);
   DisposeWindow(theWindow);
}

Setting Up the Video Bottleneck Functions

Listing 5-5 shows how to set up the video bottleneck functions of the sequence grabber video channel component. For more information on the video bottleneck functions, see "Utility Functions for Video Channel Callback Functions" beginning on page 5-98. Inside the main event loop in Listing 5-4, you should add the following lines after you call the SGSetMaximumRecordTime function (described on page 5-51).

Listing 5-5 Setting up the video bottleneck functions

   if (videoChannel) {
      err = setupVideoBottlenecks (videoChannel, theWindow,
                                  &tempPort);
      CheckError(err, "\pCouldn't set video bottlenecks");
   }

Drawing Information Over Video Frames During Capture

Listing 5-6 shows how to use the video bottleneck functions of the sequence grabber video channel component to draw the letters "QT" over each video frame as it is captured.

Listing 5-6 Drawing information over video frames during capture

pascal ComponentResult myGrabFrameComplete (SGChannel c, 
                                           short bufferNum,
                                           Boolean *done, 
                                           long refCon)
{
   ComponentResult err;

   /* call the default grab-complete function */
   err = SGGrabFrameComplete (c, bufferNum, done);
   if (*done) {

      /* frame is done */
      CGrafPtr savePort;
      GDHandle saveGD;
      PixMapHandle bufferPM, savePM;
      Rect bufferRect;
      CGrafPtr tempPort = (CGrafPtr)refCon;

      /* set to our temporary port */
      GetGWorld (&savePort, &saveGD);
      SetGWorld (tempPort, nil);

      /* find out about this buffer */
      err = SGGetBufferInfo (c, bufferNum, &bufferPM, &bufferRect,
                              nil, nil);
      if (!err) {

         /* set up to draw into this buffer */
         savePM = tempPort->portPixMap;
         SetPortPix(bufferPM);

         /* draw some text into the buffer */
         TextMode (srcXor);
         MoveTo (bufferRect.right - 20, bufferRect.bottom - 14);
         DrawString ("\pQT");
         TextMode(srcOr);
         /* restore temporary port */
         SetPortPix (savePM);
      }
      SetGWorld (savePort, saveGD);
   }
   return err;
}
OSErr setupVideoBottlenecks (SGChannel videoChannel, WindowPtr w,
                            CGrafPtr tempPort)
{
   OSErr err;
   err = SGSetChannelRefCon (videoChannel, (long)tempPort);
   if (!err) {
      VideoBottles vb;

      /* get the current bottlenecks */
      vb.procCount = 9;
      err = SGGetVideoBottlenecks (videoChannel, &vb);
      if (!err) {
         /* add our GrabFrameComplete function */
         vb.grabCompleteProc = myGrabFrameComplete;
         err = SGSetVideoBottlenecks (videoChannel, &vb);

         /* set up the temporary port */
         OpenCPort (tempPort);   /* create a temporary port 
                                    for drawing */
         SetRectRgn (tempPort->visRgn, -32000, -32000, 32000,
                      32000);    /* with a wide open visible 
                                    and clip region . . . */
         CopyRgn (tempPort->visRgn, tempPort->clipRgn);
                                 /* so that you can use it in
                                    any video buffer */
         PortChanged ((GrafPtr)tempPort);
                                 /* tell QuickDraw about the
                                    changes */
      }
   }

   return err;
}

Subtopics
Previewing and Recording Captured Data
Previewing
Recording
Playing Captured Data and Saving It in a QuickTime Movie
Initializing a Sequence Grabber Component
Creating a Sound Channel and a Video Channel
Previewing Sound and Video Sequences in a Window
Capturing Sound and Video Data
Setting Up the Video Bottleneck Functions
Drawing Information Over Video Frames During Capture

Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996




Navigation graphic, see text links

Main | Top of Section | What's New | Apple Computer, Inc. | Find It | Feedback | Help